/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.base.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationResult;

import com.cqcis.uip.base.framework.UIPServiceException;
import com.cqcis.uip.base.framework.UIPTaskException;
import com.cqcis.uip.base.framework.base.adaptor.AbstractRegisteredService;
import com.cqcis.uip.base.framework.common.Role;
import com.cqcis.uip.base.framework.common.Task;
import com.cqcis.uip.base.framework.core.RegisteredServiceContext;
import com.cqcis.uip.base.framework.core.ServiceRole;
import com.cqcis.uip.base.framework.core.ServiceRoleIgnore;
import com.cqcis.uip.base.framework.core.ServletContainer;
import com.cqcis.uip.base.framework.core.TaskResultProcessor;
import com.cqcis.uip.base.framework.core.impl.ServletContainerServer.URLRegister;
import com.cqcis.uip.base.framework.handlers.CommonService;
import com.cqcis.uip.base.framework.utils.AnnotationUtil;
import com.cqcis.uip.base.framework.utils.StringUtil;
import com.cqcis.uip.base.logadaptor.LogConfiguration;

/**
 * HTTPINVOKER
 * 
 * @author huangmy
 * @date   2009-3-25
 */
public class DefaultHttpInvokerExporter extends AbstractRegisteredService {
	private static Log log = LogFactory.getLog(DefaultHttpInvokerExporter.class);
	private static final long serialVersionUID = 1L;
	private Collection<Object> serviceTargets = new ArrayList<Object>();// implemention targets
	private int port; // 端口
	private String contextPath;
	private DefaultCommoner commoner;
	private List<URLRegister> urlRegisters = new ArrayList<URLRegister>(); // URL注册
	
	/**
	 * 上下文路径
	 * 
	 * @return
	 */
	protected String getContextPath() {
		return this.contextPath;
	}
	
	@Override
	public void start(RegisteredServiceContext serviceContext) throws Exception {
		if (this.serviceTargets.size() <= 0) {
			throw new UIPServiceException("webServiceTarget Can't be null.");
		}
		
		super.start(serviceContext);
		
		for (Object serviceTarget : serviceTargets) {
			URLRegister reg = doHttpInvokerStart(serviceTarget, serviceContext);
			if (reg != null) {
				this.urlRegisters.add(reg);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private String calledMethodDesc(RemoteInvocation invocation, Object target, RemoteInvocationResult result) {
		StringBuffer sb = new StringBuffer();
		sb.append(target.getClass().getName());
		sb.append(".");
		sb.append(invocation.getMethodName());
		sb.append("(");
		Class[] paramTypes = invocation.getParameterTypes();
		Object[] params = invocation.getArguments();
		
		if (paramTypes != null && paramTypes.length > 0) {
			for (int i = 0; i < paramTypes.length; i ++) {	
				Class type = paramTypes[i];
				if (type == null) {
					continue;
				}
				
				sb.append(type.getSimpleName());
				sb.append(" {");
				if (i < params.length && params[i] != null) {
					sb.append(params[i].toString());
				} else {
					sb.append("null");
				}
				
				sb.append("}");
				
				if (i < paramTypes.length - 1) {
					sb.append(",");
				}							
			}
		}
		
		sb.append("); RESULT >>> ");
		if (result == null) {
			sb.append("null");
		} else {
			String rt = result.getValue() != null ? result.getValue().toString() : "";
			sb.append(rt);
			String et = "";
			if (result.getException() != null) {
				et = result.getException().getClass().getName();
			}
			
			sb.append(et);			
		}
		
		return sb.toString();
	}
	
	/**
	 * 获取HTTP INVOKER
	 * 
	 * @param serviceTarget
	 * @param serviceClazz
	 * @param serviceContext
	 * @return
	 */
	protected HttpInvokerServiceExporter getServiceExporter(final boolean invokelog, Object serviceTarget, 
			Class<?> serviceClazz, final RegisteredServiceContext serviceContext) {
		HttpInvokerServiceExporter serviceExporter = new HttpInvokerServiceExporter() {
			@SuppressWarnings("unchecked")
			@Override
			protected RemoteInvocationResult invokeAndCreateResult(
					RemoteInvocation invocation, Object targetObject) {
				List<LogConfiguration> oldLogConfigr = serviceContext.getLogService().getLogConfiguration(Thread.currentThread());;
				String oldThreadName = Thread.currentThread().getName();
				RemoteInvocationResult result = null;
				long beginTime = System.currentTimeMillis();
				
				try {
					LogConfiguration logConfig = serviceContext.getRegisteredService().getLogConfiguration();
					Thread.currentThread().setName(serviceContext.getRegisteredService().getName() + "-" + oldThreadName);					
					serviceContext.getLogService().unregister(Thread.currentThread());
					serviceContext.getLogService().register(logConfig);
					
					result = super.invokeAndCreateResult(invocation, targetObject);					
					return result;
				} finally {
					long endTime = System.currentTimeMillis();
					if (invokelog && result != null && result.getException() != null) {
						log.error("Catch Exception during invoke Method: " 
								+ calledMethodDesc(invocation, getService(), result)
								+ "; ExecuteTime: " + (endTime - beginTime) + "ms", 
								result.getException());
					} else if (invokelog && log.isDebugEnabled()) {
						log.debug("Service Method Called: >>> "
								+ calledMethodDesc(invocation, getService(), result) 
								+ "; ExecuteTime: " + (endTime - beginTime) + "ms");
					}
					
					if (oldLogConfigr != null) {
						serviceContext.getLogService().unregister();
						for (LogConfiguration config : oldLogConfigr) {
							serviceContext.getLogService().register(config);
						}
					} else {
						serviceContext.getLogService().unregister();
					} 
					
					Thread.currentThread().setName(oldThreadName);
				}
			}
		};
		serviceExporter.setService(serviceTarget);
		serviceExporter.setServiceInterface(serviceClazz);
		serviceExporter.afterPropertiesSet();
		
		return serviceExporter;
	}
	
	/**
	 * 启动
	 * 
	 * @param serviceTarget
	 * @param serviceContext
	 * @throws Exception
	 */
	private URLRegister doHttpInvokerStart(Object serviceTarget, final RegisteredServiceContext serviceContext) throws Exception {
		// 获取需要发布的WebService服务接口
		Metas meta = getExportInterfaceForService(serviceTarget);
		if (meta.ignore) {
			return null;
		}
		final Class<?> serviceClazz = meta.serviceInterface;
				
		if (serviceTarget instanceof com.cqcis.uip.base.framework.core.Service) {
			((com.cqcis.uip.base.framework.core.Service) serviceTarget).start(serviceContext);
		}
				
		// Find the server
		ServletContainer server = serviceContext.getServletContainer(this.port);
		
		final HttpInvokerServiceExporter serviceExporter = getServiceExporter(meta.invokelog,serviceTarget, serviceClazz, serviceContext);
		
    	// 应用地址
    	String metaUrl = meta.url.startsWith("/") ? meta.url : ("/" + meta.url);
    	URLRegister urlReg = registerContextURL(server);
    	URLRegister urlRegister = urlReg != null ? urlReg : server.registerURL(this.contextPath, metaUrl, 
    	new HttpServlet() {
			private static final long serialVersionUID = 1L;
			@Override
    		protected void service(HttpServletRequest request,
    				HttpServletResponse response) throws ServletException,
    				IOException {
				serviceExporter.handleRequest(request, response);
			}
    	}, serviceClazz);
    	
		if (serviceTarget instanceof CommonService && this.commoner != null) {
    		CommonService serviceComm = (CommonService)serviceTarget;
    		this.commoner = new DefaultCommoner();
    		commmonSettings(this.commoner, serviceComm);
    		
    		this.commoner.start(serviceContext);
    	}
		
		return urlRegister;
	}
	
	/**
	 * 注册context
	 * 
	 * @return
	 */
	protected URLRegister registerContextURL(ServletContainer server) throws Exception {
		return null;
	}
	
	private void commmonSettings(DefaultCommoner common, CommonService serviceComm) {
		common.setBundleContext(this.bundleContext);
		common.registerLogConfiguration(this.getLogConfiguration());
		common.setCommonService(serviceComm);
		common.setEndpointExpression(this.endpointExpression);
		common.setInterfaceId(this.interfaceId);
		common.setInterfaceName(this.interfaceName);
		common.setIntervalTime(this.intervalTime);
		common.setLogDir(this.logDir);
		common.setLogLevel(this.logLevel);
		common.setStreamLogOutputService(this.streamLogOutputService);
		common.setThreadNumberExtendTaskResultProcessor(this.threadNumberExtendTaskResultProcessor);
		common.setThreadNumberSchedule(this.threadNumberTaskDispatcher);
		common.setThreadNumberTaskDispatcher(this.threadNumberTaskDispatcher);
		common.setToEndpointExpression(this.toEndpoint.getExpression());
		common.setThreadNumberTaskResultDispatcher(this.threadNumberTaskResultDispatcher);
		common.setWaitTaskMax(this.waitTaskMax);
	}
	
	@Override
	public void stop(RegisteredServiceContext serviceContext) throws Exception {
		for (Object serviceTarget : serviceTargets) {
			if (serviceTarget instanceof com.cqcis.uip.base.framework.core.Service) {
				((com.cqcis.uip.base.framework.core.Service) serviceTarget).stop(serviceContext);
			}
		}
				
		if (this.commoner != null) {
    		this.commoner.stop(serviceContext);
    	}
		
		if (this.urlRegisters != null) {
			for (URLRegister reg : this.urlRegisters) {
				reg.close();
			}			
		}
				
		super.stop(serviceContext);
	}
	
	@Override
	protected boolean doSchedule() {		
		return doScheduleSync(getTaskQueue().poll());
	}
	
	@Override
    protected boolean doScheduleSync(Task task) {
		if (this.commoner == null) {
			throw new UIPServiceException("the service maybe not implements CommonService.");
		}
    	try {
			return this.commoner.scheduleSync(task);
		} catch (Exception e) {
			throw new UIPTaskException(e);
		}
    }
	
	@Override
	public boolean processResult(final Task result) {		
		for (final Object serviceTarget : serviceTargets) {
			if (serviceTarget instanceof TaskResultProcessor) {
				getExtendProcessResultThreadPoolExecutor().execute(
						new Runnable() {
							public void run() {
								try {
									((TaskResultProcessor) serviceTarget)
											.processResult(result);
								} finally {
									afterProcessResult(result);
								}
							}
						});
			} else {
				afterProcessResult(result);
			}
		}
		
		return true;
	}
	
	/**
	 * 获取需要发布的接口
	 * 
	 * @param target
	 * @return
	 */
	private Metas getExportInterfaceForService(Object target) throws ClassNotFoundException {
		Metas meta = new Metas();
		// 1. 当前class上查找
		ServiceRole serviceRole = target.getClass().getAnnotation(ServiceRole.class);
		// 2. 如果当前class上没有, 查找当前class实现的interface
		serviceRole = serviceRole != null ? serviceRole : ((ServiceRole)AnnotationUtil.findDeepAnnotationForClass(target.getClass(), 
				ServiceRole.class, ServiceRoleIgnore.class));
		if (serviceRole == null) {
			throw new UIPServiceException("无法获取Service元数据配置信息，请在要发布的Interface上标识注解.");
		}
				
		Metas metaClass = findExportMetas(serviceRole, target);
		meta.url = metaClass.url;
		meta.serviceInterface = metaClass.serviceInterface;
		meta.invokelog = metaClass.invokelog;
		meta.ignore = metaClass.ignore;
		// 查找URL
		if (!StringUtil.hasText(meta.url)) {
			// 如果当前class上所配置的url为空, 继续查找所实现的接口的URL
			Class<?> clazzInterface = AnnotationUtil.findDeepAnnotationInterfaceForClass(target.getClass(), ServiceRole.class);
			if (clazzInterface != null) {
				serviceRole = clazzInterface.getAnnotation(ServiceRole.class);
				if (serviceRole != null) {
					Metas metaInterface = findExportMetas(serviceRole, clazzInterface);
					if (StringUtil.hasText(metaInterface.url)) {							
						meta.url = metaInterface.url;
					} else {
						// 没有指定URL, 则使用接口名作为URL
						meta.url = clazzInterface.getSimpleName();
					}
				}
			}
		}
		
		// 查找ServiceInterface
		if (meta.serviceInterface == null) {
			// 如果当前class上所配置的url为空, 继续查找所实现的接口的URL
			Class<?> clazzInterface = AnnotationUtil.findDeepAnnotationInterfaceForClass(target.getClass(), ServiceRole.class);
			if (clazzInterface != null) {
				serviceRole = clazzInterface.getAnnotation(ServiceRole.class);
				if (serviceRole != null) {
					Metas metaInterface = findExportMetas(serviceRole, clazzInterface);
					if (metaInterface.serviceInterface != null) {							
						meta.serviceInterface = metaInterface.serviceInterface;
					} else {
						// 没有指定ServiceInterface, 则使用接口className
						meta.serviceInterface = clazzInterface;
					}
				}
			}
		}
				
		return meta;
	}
	
	private Metas findExportMetas(ServiceRole serviceRole, Object target) throws ClassNotFoundException {
		Metas meta = new Metas();
		
		String[] props = serviceRole.metas();
		if (props == null || props.length <= 0) {
			return meta;
		}
				
		for (String t : props) {
			String[] names = t.split("=");
			if (names == null || names.length != 2) {
				throw new UIPServiceException("注解声明配置错误,key=value对必须完整：" + t);
			}
			
			String key = StringUtil.trimWhiteToNull(names[0]);			
			if ("serviceInterface".equalsIgnoreCase(key)) {
				String clazz = names[1];
				meta.serviceInterface = target.getClass().getClassLoader().loadClass(clazz);
			} else if ("url".equalsIgnoreCase(key)) {
				meta.url = names[1];
			} else if ("invokelog".equalsIgnoreCase(key)) {
				meta.invokelog = Boolean.parseBoolean(names[1]);
			} else if ("ignore".equalsIgnoreCase(key)) {
				meta.ignore = Boolean.parseBoolean(names[1]);
			}
		}
		
		return meta;
	}
	
	/**
	 * 元数据描述
	 * 
	 * @author huangmy
	 * @date   2009-3-26
	 */
	private class Metas {
		String url = "";
		Class<?> serviceInterface;
		boolean invokelog = true;
		boolean ignore = false;
	}
    	
	public Role getRole() {		
		return Role.HTTPINVOKER_EXPORT;
	}

	@ServiceRole(Role.HTTPINVOKER_EXPORT)
	public void setHttpInvokerExportServiceTarget(Object serviceTarget) {
		this.serviceTargets.add(serviceTarget);
	}

	/**
	 * 端口
	 * 
	 * @param port
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * 路径
	 * 
	 * @param contextPath
	 */
	public void setContextPath(String contextPath) {
		this.contextPath = contextPath;
		this.contextPath = this.contextPath.startsWith("/") ? this.contextPath : ("/" + this.contextPath);
	}
}
